﻿using HslCommunication.Profinet.Siemens;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ZhonTai.Admin.Core.Attributes;
using ZhonTai.Admin.Tools.Cache;
using ZhonTai.Industry.Contracts.Core.Enums;
using ZhonTai.Industry.Core.Consts;
using ZhonTai.Industry.Core.Enums;
using ZhonTai.Industry.Domain.BlockOffset;
using ZhonTai.Industry.Domain.WorkOrderTask;
using ZhonTai.Industry.Domain.PLCBlock;
using ZhonTai.Industry.Domain.ProductModel;
using ZhonTai.Industry.Domain.ProductSeries;
using ZhonTai.Industry.Domain.StationFeature;
using ZhonTai.Industry.Domain.StationPlc;
using ZhonTai.Industry.Domain.WorkStation;
using ZhonTai.Industry.Repositories.BlockOffset;
using ZhonTai.Industry.Repositories.WorkOrderTask;
using ZhonTai.Industry.Repositories.PLCBlock;
using ZhonTai.Industry.Repositories.ProductModel;
using ZhonTai.Industry.Repositories.ProductRoute;
using ZhonTai.Industry.Repositories.ProductSeries;
using ZhonTai.Industry.Repositories.WorkStationPlc;
using Microsoft.Extensions.Logging;
using ZhonTai.Industry.Contracts.Interactive;
using ZhonTai.Industry.Contracts.Model.Entity;
using ZhonTai.Industry.Contracts.Model.Dic;
using ZhonTai.Industry.Contracts.Communication;

namespace ZhonTai.Industry.Services.MesInit
{
    /// <summary>
    /// 设备通讯依赖参数
    /// </summary>
    [InjectTransient]
    public class MesInit : IMESInit
    {
        private ILogger<ProcessMonitor> _loggerOfProcessParameter;
        private ILogger<AlarmMonitor> _loggerOfAlarmMonitor;
        private readonly ICacheTool _cacheTool;
        private readonly PLCBlockRepository _plcBlockRepository;
        private readonly BlockOffsetRepository _blockOffsetRepository;
        private readonly StationPlcRepository _stationPlcRepository;
        private readonly WorkOrderTaskRepository _orderTasksRepository;
        private readonly ProductModelRepository _productModelRepository;
        private readonly ProductRouteRepository _productRouteRepository;
        private readonly ProductSeriesRepository _productSeriesRepository;

        /// <summary>
        /// </summary>
        public MesInit(
            ILogger<ProcessMonitor> loggerOfProcessParameter,
            ILogger<AlarmMonitor> loggerOfAlarmMonitor,
            ICacheTool cacheTool,
            PLCBlockRepository plcBlockRepository,
            BlockOffsetRepository blockOffsetRepository,
            StationPlcRepository stationPlcRepository,
            WorkOrderTaskRepository orderTasksRepository,
            ProductModelRepository productModelRepository,
            ProductRouteRepository productRouteRepository,
            ProductSeriesRepository productSeriesRepository)
        {
            _cacheTool = cacheTool;
            _plcBlockRepository = plcBlockRepository;
            _blockOffsetRepository = blockOffsetRepository;
            _stationPlcRepository = stationPlcRepository;
            _orderTasksRepository = orderTasksRepository;
            _productModelRepository = productModelRepository;
            _productRouteRepository = productRouteRepository;
            _productSeriesRepository = productSeriesRepository;
            _loggerOfProcessParameter = loggerOfProcessParameter;
            _loggerOfAlarmMonitor = loggerOfAlarmMonitor;
        }

        /// <summary>
        /// 初始化产品相关数据
        /// </summary>
        /// <returns></returns>
        public async Task InitProductDataAsync()
        {
            //两种方式1：有订单，2：无订单
            //有订单：根据订单获取产品系列获取产品详情
            //无订单：根据开启的产品系列获取产品详情
            //设置当前开启订单
            WorkOrderTaskEntity orderTasks = await _orderTasksRepository.Select.Where(w => w.State == OrderEnum.InProgress).ToOneAsync();
            ProductModelEntity? productModel = null;
            if (orderTasks != null)
            {
                await _cacheTool.SetAsync(CacheKey.OrderId, orderTasks.OrderId);
                //设置当前订单的产品路线
                productModel = await _productModelRepository.Select.Where(w => w.Id == orderTasks.ProductModelId).ToOneAsync();
            }
            else
            {
                ProductSeriesEntity productSerices = await _productSeriesRepository.Select.Where(w => w.Enabled).ToOneAsync();
                if (productSerices != null)
                {
                    productModel = await _productModelRepository
                        .Select
                        .Where(w => w.ProductSeriesId == productSerices.Id && w.Enabled)
                        .Include(i => i.ProductRoute)
                        .Include(i => i.ProductSeries)
                        .Include(i => i.DeviceFormula)
                        .ToOneAsync();
                }
            }
            if (productModel != null)
            {
                var productRoutes = await _productRouteRepository
                    .Select
                    .Where(w => w.Id == productModel.ProductRouteId)
                    .IncludeMany(i => i.RouteDetailList)
                    .ToOneAsync();
                var tasks = productRoutes?.RouteDetailList.Select(async t =>
                {
                    await _cacheTool.SetAsync(CacheKey.ProductVerify + t.StationId, t);
                }).ToList();
                if (tasks != null)
                {
                    await Task.WhenAll(tasks);
                }
                await _cacheTool.SetAsync(CacheKey.ProductId, productModel.Id);
            }
        }

        /// <summary>
        /// 启动工艺路线
        /// </summary>
        /// <param name="workStation"></param>
        /// <param name="stationFeature"></param>
        /// <returns></returns>
        public async Task InitProductRouteAsync(WorkStationEntity workStation, StationFeatureEntity stationFeature)
        {
            await _cacheTool.SetAsync(CacheKey.VerifyStatus + workStation.Id, stationFeature.Enabled);
        }

        /// <summary>
        /// 初始化工艺采集交互
        /// </summary>
        /// <param name="workStation"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<bool> InitProcessParmeterAsync(WorkStationEntity workStation)
        {
            if (!InstanceDic.PLC.TryGetValue(workStation.Id, out var value))
            {
                await InitPlcAsync(workStation);
            }
            DataShip dataShip = new DataShip();
            dataShip.StationId = workStation.Id;
            dataShip.OPCode = workStation.StationCode;
            List<PLCBlockEntity> plcBlockList = await _plcBlockRepository.Select
                .Where(w => w.WorkStationId == workStation.Id && w.Purpose == DBPurposeEnum.Gather && w.Enabled)
                .Include(i => i.WorkStation)
                .ToListAsync();
            if (plcBlockList.Count != 2)
            {
                throw new Exception("用于采集的数据块当前工站必须且只能是两个，一个用于PLC一个用于MES！");
            }
            plcBlockList.ForEach(plcBlock =>
            {
                List<BlockOffsetEntity> blockOffsetList = _blockOffsetRepository.Select
                 .Where(w => w.BlockId == plcBlock.Id && w.Enabled)
                 .Include(i => i.PLCBlock)
                 .Include(i => i.PLCOffset)
                 .OrderBy(o => o.Sort)
                 .ToList();
                if (plcBlock.BlockType == DataBlockEnum.PLC)
                {
                    DB plcDB = new DB();
                    plcDB.DBAddress = plcBlock.Address;
                    plcDB.StartOffset = plcBlock.StartOffset;
                    plcDB.DBLength = plcBlock.DataSize;
                    plcDB.KeyFields = blockOffsetList.FindAll(f => f.IsNoticed)
                     .Select(s => new DBField()
                     {
                         Another = string.IsNullOrWhiteSpace(s.Another) ? throw new Exception($"{nameof(s.Another)}参数不能为空！") : s.Another,
                         Offset = s.PLCOffset.ByteOffset,
                         PLCDataType = s.PLCOffset.DataType,
                         GroupName = s.GroupName,
                         Description = s.Description,
                         Topic = s.Topic,
                         Unit = s.Unit,
                     }).ToList();
                    plcDB.DataFields = blockOffsetList.FindAll(f => !f.IsNoticed)
                     .Select(s => new DBField()
                     {
                         Another = string.IsNullOrWhiteSpace(s.Another) ? throw new Exception($"{nameof(s.Another)}参数不能为空！") : s.Another,
                         Offset = s.PLCOffset.ByteOffset,
                         PLCDataType = s.PLCOffset.DataType,
                         GroupName = s.GroupName,
                         Description = s.Description,
                         Topic = s.Topic,
                         Unit = s.Unit,
                     }).ToList();
                    dataShip.PLCDB = plcDB;
                }
                if (plcBlock.BlockType == DataBlockEnum.MES)
                {
                    DB mesDB = new DB();
                    mesDB.DBAddress = plcBlock.Address;
                    mesDB.DBLength = plcBlock.DataSize;
                    mesDB.KeyFields = blockOffsetList.FindAll(f => f.IsNoticed)
                     .Select(s => new DBField()
                     {
                         Another = string.IsNullOrWhiteSpace(s.Another) ? throw new Exception($"{nameof(s.Another)}参数不能为空！") : s.Another,
                         Offset = s.PLCOffset.ByteOffset,
                         PLCDataType = s.PLCOffset.DataType,
                         GroupName = s.GroupName,
                         Description = s.Description,
                         Topic = s.Topic,
                         Unit = s.Unit,
                     }).ToList();
                    mesDB.DataFields = blockOffsetList.FindAll(f => !f.IsNoticed)
                     .Select(s => new DBField()
                     {
                         Another = string.IsNullOrWhiteSpace(s.Another) ? throw new Exception($"{nameof(s.Another)}参数不能为空！") : s.Another,
                         Offset = s.PLCOffset.ByteOffset,
                         PLCDataType = s.PLCOffset.DataType,
                         GroupName = s.GroupName,
                         Description = s.Description,
                         Topic = s.Topic,
                         Unit = s.Unit,
                     }).ToList();
                    dataShip.MESDB = mesDB;
                }
            });
            return await Task.Run(() =>
            {
                if (InstanceDic.ProcessDic.TryGetValue(workStation.Id, out var value))
                {
                    value.Stop();
                    InstanceDic.ProcessDic.TryRemove(workStation.Id, out var removedValue);
                }
                int partitions = dataShip.PLCDB.DataFields.Count + dataShip.PLCDB.KeyFields.Count;

                ProcessMonitor processMonitor = new ProcessMonitor(_loggerOfProcessParameter, partitions);
                InstanceDic.ProcessDic.TryAdd(workStation.Id, processMonitor);
                processMonitor.Initialization(dataShip);
                processMonitor.Start();
                return true;
            });
        }

        /// <summary>
        /// 停止工艺采集交互
        /// </summary>
        /// <param name="workStation"></param>
        /// <returns></returns>
        public async Task<bool> StopProcessParmeterAsync(WorkStationEntity workStation)
        {
            return await Task.Run(() =>
            {
                if (InstanceDic.ProcessDic.TryGetValue(workStation.Id, out var value))
                {
                    value.Stop();
                    return InstanceDic.ProcessDic.TryRemove(workStation.Id, out var removedValue);
                }
                return false;
            });
        }

        /// <summary>
        /// 停止告警监控
        /// </summary>
        /// <param name="workStation"></param>
        /// <returns></returns>
        public async Task<bool> StopAlarmErrAsync(WorkStationEntity workStation)
        {
            return await Task.Run(() =>
            {
                if (InstanceDic.AlarmDic.TryGetValue(workStation.Id, out var value))
                {
                    value.Stop();
                    return InstanceDic.AlarmDic.TryRemove(workStation.Id, out var removedValue);
                }
                return false;
            });
        }

        /// <summary>
        /// PLC设备初始化
        /// </summary>
        /// <param name="workStation"></param>
        /// <returns></returns>
        public async Task<bool> InitPlcAsync(WorkStationEntity workStation)
        {
            StationPlcEntity stationPlc = await _stationPlcRepository.Select
           .Where(w => w.WorkStationId == workStation.Id && w.Enabled)
           .Include(i => i.PLCDevice)
           .ToOneAsync();
            if (stationPlc != null)
            {
                return await Task.Run(() =>
                {
                    Enum.TryParse(stationPlc.PLCDevice.Model, true, out SiemensPLCS siemensPLCEnum);
                    SiemensPlc siemensPlc = new SiemensPlc(siemensPLCEnum, stationPlc.IpAddress, stationPlc.Port, stationPlc.RackId, stationPlc.SlotId);
                    return InstanceDic.PLC.TryAdd(workStation.Id, siemensPlc);
                });
            }
            return false;
        }

        /// <summary>
        /// 报警数据初始化
        /// </summary>
        /// <param name="workStation"></param>
        /// <returns></returns>
        public async Task<bool> InitAlarmErrAsync(WorkStationEntity workStation)
        {
            if (!InstanceDic.PLC.TryGetValue(workStation.Id, out var value))
            {
                await InitPlcAsync(workStation);
            }
            DataShip dataShip = new DataShip();
            dataShip.StationId = workStation.Id;
            dataShip.OPCode = workStation.StationCode;
            List<PLCBlockEntity> plcBlockList = await _plcBlockRepository.Select
                .Where(w => w.WorkStationId == workStation.Id && w.Purpose == DBPurposeEnum.AlarmErr && w.Enabled)
                .Include(i => i.WorkStation)
                .ToListAsync();
            if (plcBlockList.Count != 1)
            {
                throw new Exception("用于报警监控的数据块当前工站必须且只能是一个，并且所属方必须是PLC！");
            }
            plcBlockList.ForEach(w =>
            {
                if (w.BlockType == DataBlockEnum.PLC)
                {
                    DB plcDB = new DB();
                    plcDB.DBAddress = w.Address;
                    plcDB.StartOffset = w.StartOffset;
                    plcDB.DBLength = w.DataSize;
                    dataShip.PLCDB = plcDB;
                }
            });
            return await Task.Run(() =>
             {
                 if (InstanceDic.AlarmDic.TryGetValue(workStation.Id, out var value))
                 {
                     value.Stop();
                     InstanceDic.AlarmDic.TryRemove(workStation.Id, out var removedValue);
                 }
                 AlarmMonitor alarmMonitor = new AlarmMonitor(_loggerOfAlarmMonitor, 1000);
                 InstanceDic.AlarmDic.TryAdd(workStation.Id, alarmMonitor);
                 alarmMonitor.Initialization(dataShip);
                 alarmMonitor.Start();
                 return true;
             });
        }
    }
}